package com.honey.compilation.sql.dml;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;

import com.honey.compilation.sql.SqlColumn;
import com.honey.compilation.sql.SqlKeyWord;
import com.honey.compilation.sql.SqlScript;
import com.honey.compilation.sql.SqlTable;
import com.honey.core.builder.CharacterBuilder;
import com.honey.core.builder.CharacterBuilderFactory;
import com.honey.core.compiler.CompilationException;
import com.honey.core.compiler.IndentSpace;
import com.honey.core.utils.StringUtility;


/**
 * 连接查询. SQL 92中定义的语法如下:
 * 
 * @author Administrator
 *
 */
public class JoinSelect extends AbstractSelect implements SqlScript{


	/** 是否去除重复列 */
	private boolean distinct ;
	
	/** 是否使用随机的表别名,默认开启 */
	//private boolean isRandomAlias  ;
	
	/** 查询条件 */
	private Condition condition ;
	
	/** 查询对象 */
	private Map<SqlTable, SelectObject> selectObjects ;
	
	/**
	 * 构造函数
	 * @param tableName 表
	 */
	public JoinSelect(String tableName){
		this(new SqlTable(tableName));
	}
	
	/**
	 * 构造函数
	 * @param schema 数据库结构
	 * @param tableName 表名称
	 */
	public JoinSelect(String schema ,String tableName){
		this(new SqlTable(schema,tableName));
	}
	
	/**
	 * 构造函数
	 * @param schema 数据库结构
	 * @param tableName 数据库表
	 * @param alias 别名
	 */
	public JoinSelect(String schema ,String tableName,String alias){
		this(new SqlTable(schema,tableName),alias, false);
	}
	
	/**
	 * 构造函数
	 * @param table 数据库表
	 * @param alias 别名
	 */
	public JoinSelect(SqlTable table,String alias){
		this(table,alias,false);
	}
	/**
	 * 
	 * @param tableName table name
	 * @param alias 别名
	 * @param distinct 去除重复列 true:是  false:否
	 */
	public JoinSelect(SqlTable table,String alias, boolean distinct){
		this.selectObjects = new LinkedHashMap<SqlTable, SelectObject>();
		this.distinct =distinct ;
		//this.isRandomAlias= true;
		
		if(table != null ){
			SelectObject so = new SelectObject(table) ;
			so.setAlias(alias);
			this.selectObjects.put(table ,so);
		}
	}
	
	/**
	 * 
	 * @param tableName table name
	 * @param distinct 去除重复列 true:是  false:否
	 */
	public JoinSelect(SqlTable table, boolean distinct){
		this(table,null,distinct);
	}
	
	/**
	 * 
	 * @param tableName table name 
	 * @param alias 表述别名
	 * @param columns 查询列
	 */
	public JoinSelect(SqlTable table,String alias,SqlColumn ...columns  ){
		this(table,alias,false);
		addColumn(columns);
	}
	
	
	/**
	 * 
	 * @param tableName table name 
	 * @param columns 查询列
	 */
	public JoinSelect(SqlTable table,SqlColumn ...columns  ){
		this(table,null,false);
		addColumn(columns);
	}
	
	/**
	 * 条件
	 * @return
	 */
	public Condition getCondition() {
		return condition;
	}
	
	/**
	 * 条件
	 * @param condition
	 */
	public void setCondition(Condition condition) {
		this.condition = condition;
	}
	
	/**
	 * 添加多表查询
	 * @param tableName 新表的名称 
	 * @param alias 新表的别名
	 * @param columns 新表查询列
	 */
	public void addTableColumn(SqlTable table,String alias , SqlColumn ...columns ){
		if( table != null ){
			SelectObject so = this.selectObjects.get(table);
			if(so == null){
				so= new SelectObject(table,columns);
				so.setAlias(alias);
				this.selectObjects.put(table, so);
			}else{
				so.addColumns(columns);
			}
		}
	}
	
	/**
	 * 添加多表查询
	 * @param tableName 新表的名称
	 * @param columns 新表查询列
	 */
	public void addTableColumn(SqlTable table,SqlColumn ...columns ){
		addTableColumn(table, null, columns);
	}
	
	/**
	 * 添加查询列
	 * @param columns 查询列
	 */
	public void addColumn(SqlColumn ...columns ){
		if(this.selectObjects.size()>0){
			Iterator<SqlTable> it = this.selectObjects.keySet().iterator();
			if( it.hasNext()){
				SqlTable tableName = it.next() ;
				SelectObject so = this.selectObjects.get( tableName );
				if( so == null){
					so = new SelectObject(tableName,columns);
					this.selectObjects.put(tableName, so);
				}else{
					so.addColumns(columns);
				}
			}
		}
		
	}
	
	/**
	 * 添加查询列
	 * @param columns 查询列
	 */
	public void addColumn(String ...columns ){
		if(this.selectObjects.size()>0){
			Iterator<SqlTable> it = this.selectObjects.keySet().iterator();
			if( it.hasNext()){
				SqlTable tableName = it.next() ;
				SelectObject so = this.selectObjects.get( tableName );
				if( so == null){
					so = new SelectObject(tableName,columns);
					this.selectObjects.put(tableName, so);
				}else{
					so.addColumns(columns);
				}
			}
		}
	}
	
	/**
	 * 是否去除重复列 true:是  false:否
	 * @return 
	 */
	public boolean isDistinct() {
		return distinct;
	}

	/**
	 * 
	 * @param distinct 是否去除重复列 true:是  false:否
	 */
	public void setDistinct(boolean distinct) {
		this.distinct = distinct;
	}

	/**
	 * 是否添加随机别名
	 * @return
	 */
//	public boolean isRandomAlias() {
//		return isRandomAlias;
//	}

	/**
	 * 是否添加随机别名
	 * @param isRandomAlias 是否添加随机别名 true:是 false:否
	 */
//	public void setRandomAlias(boolean isRandomAlias) {
//		this.isRandomAlias = isRandomAlias;
//	}

	/**
	 * inner join 连接查询(内部连接查询)
	 * @param table 连接表
	 * @param alias 连接表的别名
	 * @param onCondition 连接条件
	 */
	public void addInnerJoinTable(SqlTable table,String alias,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,alias,onCondition,Join.INNER));
	}
	
	/**
	 * inner join 连接查询(内部连接查询)
	 * @param table 连接表
	 * @param onCondition 连接条件
	 */
	public void addInnerJoinTable(SqlTable table,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,null,onCondition,Join.INNER));
	}
	
	/**
	 * left join 连接查询(左连接查询)
	 * @param table 连接表
	 * @param alias 连接表的别名
	 * @param onCondition 连接条件
	 */
	public void addLeftJoinTable(SqlTable table,String alias,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,alias,onCondition,Join.LEFT));
	}
	
	/**
	 * left join 连接查询(左连接查询)
	 * @param table 连接表
	 * @param onCondition 连接条件
	 */
	public void addLeftJoinTable(SqlTable table,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,null,onCondition,Join.LEFT));
	}
	
	/**
	 * right join 连接查询(右连接查询)
	 * @param table 连接表
	 * @param alias 连接表的别名
	 * @param onCondition 连接条件
	 */
	public void addRightJoinTable(SqlTable table,String alias,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,alias,onCondition,Join.RIGHT));
	}
	
	
	/**
	 * right join 连接查询(右连接查询)
	 * @param table 连接表
	 * @param onCondition 连接条件
	 */
	public void addRightJoinTable(SqlTable table,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,null,onCondition,Join.RIGHT));
	}
	
	/**
	 * Full join 连接查询(全连接查询)
	 * @param table 连接表
	 * @param alias 连接表的别名
	 * @param onCondition 连接条件
	 */
	public void addFullJoinTable(SqlTable table,String alias,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,alias,onCondition,Join.FULL));
	}
	
	/**
	 * Full join 连接查询(全连接查询)
	 * @param table 连接表
	 * @param onCondition 连接条件
	 */
	public void addFullJoinTable(SqlTable table,Condition onCondition){
		selectObjects.put(table,new SelectObject(table,null,onCondition,Join.FULL));
	}
	
	/**
	 * 
	 * cross join 连接查询(交叉接查询,也就是笛卡尔乘积).不建议用户使用这种查询.
	 * @param table 连接表
	 * @param alias 连接表的别名
	 */
	@Deprecated
	public void addCrossJoinTable(SqlTable table,String alias){
		selectObjects.put(table,new SelectObject(table,alias,null,Join.CROSS));
	}
	
	/**
	 * 
	 * cross join 连接查询(交叉接查询,也就是笛卡尔乘积).不建议用户使用这种查询.
	 * @param table 连接表
	 */
	@Deprecated
	public void addCrossJoinTable(SqlTable table){
		selectObjects.put(table,new SelectObject(table,null,null,Join.CROSS));
	}
	
	
	@Override
	public CharacterBuilder compiledContent(int indentLevel)throws CompilationException {
		CharacterBuilder answer = CharacterBuilderFactory.createC16StringBuilder();
		
		answer.append(super.compiledContent(indentLevel)) ;
		if(distinct){
			answer.append( SqlKeyWord.DISTINCT);
		}
		CharacterBuilder table=CharacterBuilderFactory.createC16StringBuilder();
		CharacterBuilder column=CharacterBuilderFactory.createC16StringBuilder();
		
		indentLevel++;
		if( this.selectObjects.size() >0 ){
			
			boolean comma = false;
			for (Iterator<SqlTable> it =  this.selectObjects.keySet().iterator();it.hasNext();){
				SqlTable key = it.next();
				SelectObject so = this.selectObjects.get(key);
				if (comma){
					if(so.columns.size()>0 )
						column.append(SqlKeyWord.OPERATION_COMMA);
					if( !so.isJoin() )
						table.append(SqlKeyWord.OPERATION_COMMA);
				}else comma = true;
				
				
				column.append(so.getFormattedColumn(indentLevel) );
				table.append(so.getFormattedTableName(indentLevel));
			}
			if( column.length() ==0 ){
				IndentSpace.newSpace(answer, indentLevel);
				answer.append(SqlKeyWord.OPERATION_ASTERISK);
			}else{
				answer.append(column);
			}
			
		}else{
			//return CharacterBuilderFactory.createC16StringBuilder();
			answer.append(SqlKeyWord.OPERATION_ASTERISK);
		}
		indentLevel--;
		IndentSpace.newSpace(answer, indentLevel);
		answer.append(SqlKeyWord.FROM);
		answer.append(table);
		
		if(condition != null){
			//condition.setEnableAlias(isRandomAlias);
			IndentSpace.newSpace(answer, indentLevel);
			answer.append(SqlKeyWord.WHERE);
			IndentSpace.newSpace(answer, indentLevel+1);
			answer.append(condition.compiledContent(indentLevel))
			;
		}
		answer.append(SqlKeyWord.OPERATION_SEMICOLON);
		
		return answer;
	}
	

	
	private class SelectObject{
		
		/**
		 * 查询表的名称
		 */
		private SqlTable table;
		
		/**
		 * 查询列,如果没有查询列那么使用*查询
		 */
		private java.util.Set<SelectColumn> columns ;
		
		
		/**
		 * 查询表的别名,默认使用随机别名
		 */
		private String alias ;
		
		private Condition onCondition ;
		
		private Join join;
		
		SelectObject (SqlTable table){
			this.columns= new LinkedHashSet<SelectColumn>();
			this.table = table;
		}
		
		SelectObject (SqlTable table, SqlColumn ...columns){
			this.columns= new LinkedHashSet<SelectColumn>();
			this.table = table;
			addColumns(columns);
		}
		
		SelectObject (SqlTable table, String ...columns){
			this.columns= new LinkedHashSet<SelectColumn>();
			this.table =table;
			addColumns(columns);
		}
		
		SelectObject(SqlTable table,String alias,Condition onCondition,Join join){
			this.columns= new LinkedHashSet<SelectColumn>();
			this.table = table;
			this.onCondition= onCondition;
			this.join = join;
			this.alias = alias;
		}
		
		public void setAlias(String alias) {
			this.alias = alias;
		}

		/**
		 * 添加列
		 * @param columns
		 */
		public void addColumns(SqlColumn  ...columns  ){
			if(columns == null ) return;
			
			for (SqlColumn  column : columns){
				if(column != null  ){
					if(column instanceof  SelectColumn ){
						this.columns.add((SelectColumn)column);
					}else{
						this.columns.add(new SelectColumn(column.getName().getValue()) );
					}
				}
			}
		}
		public void addColumns(String  ...columns  ){
			if(columns == null ) return;
			
			for (String  column : columns){
				if(StringUtility.stringHasValue( column) ){
					this.columns.add(new SelectColumn(column));
				}
			}
		}
		
		private String getTableAlias(){
			if(StringUtility.stringHasValue(this.alias) ){
				return this.alias;
			} /*else if( isRandomAlias){
				this.alias ="_"+ StringUtility.getRandomString(3)+"_";
				return this.alias;
			}*/else{
				return null;
			}
		}
		
		private boolean isJoin(){
			return join != null;
		}
		
		CharacterBuilder getFormattedColumn( int indentLevel) throws CompilationException{
			CharacterBuilder answer = CharacterBuilderFactory.createC16StringBuilder();
			this.alias =getTableAlias();
			boolean comma = false;
			for( SelectColumn column : columns ){
				if (comma)answer.append(SqlKeyWord.OPERATION_COMMA);else comma = true;
				
				IndentSpace.newSpace(answer, indentLevel);
				
				if( StringUtility.stringHasValue(this.alias))
					column.setTableName(this.alias);
				answer.append(column.compiledContent(indentLevel))
				;
			}
			return answer;
		}
		
		CharacterBuilder getFormattedTableName( int indentLevel) throws CompilationException{
			CharacterBuilder answer = CharacterBuilderFactory.createC16StringBuilder();
			this.alias =getTableAlias();
			if(condition != null && this.alias != null){
				condition.setTableAlias(table, alias);
			}
			IndentSpace.newSpace(answer, indentLevel);
			if( this.isJoin() ){
				answer.append( join.joinTable(table,alias,onCondition) ) ;
			}else{
				
				answer.append(table) ;
				if(StringUtility.stringHasValue(alias) ){
					answer.append(SqlKeyWord.AS) 
					.append(alias) 
					.append(SqlKeyWord.OPERATION_SPACE)
					;
				}
			}
			return answer;
		}
	}
	
	
	private enum Join {
		
		/** 内部连接 */
		INNER(SqlKeyWord.INNER),
		
		/** 左连接 */
		LEFT(SqlKeyWord.LEFT),
		
		/** 右连接 */
		RIGHT(SqlKeyWord.RIGHT),
		
		/** 全连接 */
		FULL(SqlKeyWord.FULL),
		
		/** 交叉连接(笛卡尔乘积) */
		CROSS(SqlKeyWord.CROSS),
		
		//UNDEFINE(null),
		;
		private final SqlKeyWord name;
		
		private Join(SqlKeyWord name){
			this.name = name;
		}
		
		CharacterBuilder joinTable(SqlTable table, String alias ,Condition condition ) throws CompilationException{
			CharacterBuilder answer = CharacterBuilderFactory.createC16StringBuilder(); 
			if( this==CROSS ){
				answer.append(this.name)
				.append(SqlKeyWord.JOIN)
				.append(table.compiledContent(0))
				;
			}else{
				answer.append(this.name)
				.append(SqlKeyWord.JOIN)
				.append(table.compiledContent(0))
				;
				if(StringUtility.stringHasValue(alias)){
					answer.append(SqlKeyWord.AS).append(alias)
					.append(SqlKeyWord.OPERATION_SPACE)
					;
				}
				if( condition != null ){
					answer.append(SqlKeyWord.ON);
					answer.append(condition.compiledContent(0));
				}
			}
			return answer;
		}
		
	};


}
